import React, { createContext, useContext, useEffect, useState, useMemo } from 'react'
import ReactDOM from 'react-dom'

// 1.创造一个上下文
const C = createContext(null);

function App() {
    const [a, setA] = useState(0)
    const [b, setB] = useState(10) // 可以定义多个变量共享
    useEffect(() => {
        console.log("App reload")
    })
    return (
        <div>
            <Other></Other>
            <C.Provider value={{ a, setA, b, setB }}>
                这是爷爷
                <Baba></Baba>
                <Child5></Child5>
            </C.Provider>
        </div>
    )
}

const Other = () => {
    useEffect(() => {
        console.log("Other reload")
    })
    return (
        <div>
            无关组件
        </div>
    )
}

function Baba() {
    useEffect(() => {
        console.log("Baba reload")
    })
    return (
        <div>
            这是爸爸
            <Child></Child>
            <Child2></Child2>
            <Child3></Child3>
            <Child4></Child4>
            <Child6></Child6>
            {/* {useMemo(() => Child5(), [])} */}
        </div>
    )
}
function Child() {
    // 3.使用上下文，因为传入的是对象，则接受也应该是对象,C的value中传入了啥,这里就能取出啥,取不出未定义的.
    const { a, setA } = useContext(C)
    const add = () => {
        setA(a => a + 1)
    };
    useEffect(() => {
        console.log("Child reload")
    })
    return (
        <div>
            这是1儿子:a:{a}
            <button onClick={add}>+1</button>
        </div>
    )
}

function Child2() {
    // 使用上下文，因为传入的是对象，则接受也应该是对象
    const { a, setA } = useContext(C)
    const add = () => {
        setA(a => a + 1)
    };
    useEffect(() => {
        console.log("Child2 reload")
    })
    return (
        <div>
            这是2儿子:a:{a}
            <button onClick={add}>+1</button>
        </div>
    )
}

function Child3() {
    // 使用上下文，因为传入的是对象，则接受也应该是对象
    // const { a, setA } = useContext(C)
    const [a, setA] = useState(0)
    const add = () => {
        setA(a => a + 1)
    };
    useEffect(() => {
        console.log("Child3 reload")
    })
    return (
        <div>
            这是3儿子:a:{a}
            <button onClick={add}>+1</button>
        </div>
    )
}

function Child4() {
    // 使用上下文，因为传入的是对象，则接受也应该是对象
    const { b, setB } = useContext(C)
    const add = () => {
        setB(b => b + 1)
    };
    useEffect(() => {
        console.log("Child4 reload")
    })
    return (
        <div>
            这是4儿子:b:{b}
            <button onClick={add}>+1</button>
        </div>
    )
}
// 1.尝试使用React.memo来避免此组件的重新渲染
function Child5() {
    const { b, setB } = useContext(C) // 将useContext拆出来,因为这一层肯定会重新渲染,将主要的计算和渲染层拆分后用React.mem包裹
    useEffect(() => {
        console.log("Child5 reload")
    })
    return <InnerChild5 top={b} setB={setB} />;
}
// React.memo可用于props的变更检查，当props没有发生改变时，那么该组件就不会渲染
const InnerChild5 = React.memo(({ b, setB }) => InnerChild5Fun(b, setB));
// 将原本的计算和渲染拆分到这里
const InnerChild5Fun = (b, setB) => {
    useEffect(() => {
        console.log("InnerChild5Fun reload")
    })
    const add = () => {
        setB(b => b + 1)
    };
    return (
        <div>
            这是5儿子:b:{b}
            <button onClick={add}>+1</button>
        </div>
    )
}
// 使用useMemo进行缓存组件
const InnerChild6 = (b, setB) => {
    const add = () => {
        setB(b => b + 1)
    };
    console.log("InnerChild6 reload")
    return (
        <div>
            这是6儿子:b:{b}
            <button onClick={add}>+1</button>
        </div>
    )
}

const Child6 = () => {
    const { b, setB } = useContext(C)
    useEffect(() => {
        console.log("Child6 reload")
    })
    return useMemo(() => InnerChild6(b, setB), [b, setB]);
}


const WarpApp = () => {

    useEffect(() => {
        console.log("WarpApp reload")
    })
    return (
        <div>
            <h1>外围组件</h1>
            <App />
        </div>
    )
}

// ReactDOM.render(<App />, document.getElementById('root'));
export default WarpApp

/*
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据
无论组件树多深，任何此组件树中的组件都能读取这个值。
使用useContext在改变一个数据时，是通过自己逐级查找对比改变的数据然后渲染，而不是通过数据响应式来监控变量的。
也就是说在点击+1操作后，React开始从function App开始执行代码了,观察 "Child reload" 可以看出来.
实例中的外围组件WarpApp并不会因为useContext变化而重新渲染
useContext相当于<MyContext.Consumer>或class组件中的static contextType = MyContext

当 Provider 的 value 值发生变化时，它内部的所有消费组件都会重新渲染, 关于如何避免组件重新渲染:
https://juejin.cn/post/6869340244954513421
https://github.com/facebook/react/issues/15156
*/